using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using System.Collections;


namespace _395_2d
{
    public class SpriteManager : Microsoft.Xna.Framework.DrawableGameComponent, IPublisher
    {
        private Player player;
        private ArrayList enemies;
        private ArrayList enemyProjectiles;
        private ArrayList playerProjectiles;

        private List<NPCData> spawnList;

        private BoundingBox screen;
        private BoundingBox spriteBounds;

        private ArrayList subscribers;

        private double StartTime;


        SoundEffect enemy_death = SoundEffect.FromStream(TitleContainer.OpenStream(@"Content\small_explosion.wav"));
        SoundEffect player_hit = SoundEffect.FromStream(TitleContainer.OpenStream(@"Content\player_hit.wav"));

        private bool bossSpawned = false;


        public SpriteManager(Game game, GameTime startTime)
            : base(game)
        {
            StartTime = startTime.TotalGameTime.TotalSeconds;

            screen = new BoundingBox(new Vector2(0, 0), new Vector2(800, 480));
            spriteBounds = new BoundingBox(new Vector2(0, -100), new Vector2(800, 580));

            enemies = new ArrayList();
            enemyProjectiles = new ArrayList();
            playerProjectiles = new ArrayList();
            spawnList = new List<NPCData>();

            subscribers = new ArrayList();

            Weapon[] weapons = Game.Content.Load<Weapon[]>("weapons");
            foreach (Weapon w in weapons)
            {
                Content.Weapons.Add(w.Name, w);
            }

            foreach (NPCType t in Game.Content.Load<NPCType[]>("NPCtypes"))
            {
                Content.NPCTypes.Add(t.Name, t);
            }

            foreach (NPCData d in Game.Content.Load<NPCData[]>("NPCData"))
            {
                spawnList.Add(d);
            }

            spawnList.Sort();

            player = new Player(this, "Ship", weapons[0].Name, 100, new Vector2(400, 300), new Vector2(70, 70));
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            Sprite.spriteBatch = new SpriteBatch(this.Game.GraphicsDevice);
            Content.Textures["Ship"] = Game.Content.Load<Texture2D>("Ship");
            Content.Textures["Bullet"] = Game.Content.Load<Texture2D>("Bullet");
            Content.Textures["Bullet_1"] = Game.Content.Load<Texture2D>("Bullet_1");
            Content.Textures["NPC"] = Game.Content.Load<Texture2D>("NPC");
            Content.Textures["NPC_1"] = Game.Content.Load<Texture2D>("NPC_1");
            Content.Textures["NPC_2"] = Game.Content.Load<Texture2D>("NPC_2");
            Content.Textures["NPC_3"] = Game.Content.Load<Texture2D>("NPC_3");
            Content.Textures["NPC_Boss"] = Game.Content.Load<Texture2D>("NPC_Boss");
        }

        private void processInputs(GameTime gt)
        {
            GamePadState gs = GamePad.GetState(PlayerIndex.One);

            if (gs.Buttons.Back == ButtonState.Pressed)
                this.Game.Exit();

            float x = gs.ThumbSticks.Left.X;
            x *= 8;
            float y = gs.ThumbSticks.Left.Y;
            y *= 8;

            Vector2 leftThumbStick = new Vector2(x, y);

            player.ModifyPosition(leftThumbStick);

            if (gs.Buttons.A == ButtonState.Pressed)
            {
                player.shoot(gt);
            }

            KeyboardState ks = Keyboard.GetState();

            Vector2 kMov = new Vector2(0,0);

            if (ks.IsKeyDown(Keys.Up))
                kMov.Y += 8;
            if (ks.IsKeyDown(Keys.Down))
                kMov.Y -= 8;
            if (ks.IsKeyDown(Keys.Left))
                kMov.X -= 8;
            if (ks.IsKeyDown(Keys.Right))
                kMov.X += 8;

            player.ModifyPosition(kMov);

            if (ks.IsKeyDown(Keys.Space))
                player.shoot(gt);


        }

        public override void Update(GameTime gameTime)
        {
            processInputs(gameTime);

            if (enemies.Count == 0 && spawnList.Count == 0 && bossSpawned)
            {
                foreach (ISubscriber s in subscribers)
                {
                    s.Deliver(new GameOverMessage(GameOverMessage.Outcome.VICTORY));
                }
            }

            //Update
            foreach (Bullet p in playerProjectiles)
            {
                p.Update(gameTime);
            }

            foreach (Bullet p in enemyProjectiles)
            {
                p.Update(gameTime);
            }

            foreach (Ship n in enemies)
            {
                n.Update(gameTime);
                n.shoot(gameTime);
            }

            //Add new Ships
            for (int i = spawnList.Count - 1; i >= 0; i--)
            {
                if (gameTime.TotalGameTime.TotalSeconds - StartTime < spawnList[i].SpawnTime)
                    break;

                NPCType type = (NPCType)Content.NPCTypes[spawnList[i].Type];
                enemies.Add(new NPC(this, type.Texture, type.MountedWeapons, type.Health, spawnList[i].Position, type.Size, type.Velocity, gameTime.TotalGameTime.TotalSeconds));
                spawnList.RemoveAt(i);
            }

            if (gameTime.TotalGameTime.TotalSeconds - StartTime > 90 && !bossSpawned )
            {
                enemies.Add(new Boss(this, gameTime.TotalGameTime.TotalSeconds));
                bossSpawned = true;
            }

            //Cleanup Out of Bounds Objects
            for (int i = playerProjectiles.Count - 1; i >= 0; i--)
            {
                Sprite ii = (Sprite)playerProjectiles[i];

                if (!ii.intersects(spriteBounds))
                    playerProjectiles.Remove(ii);
            }

            for (int i = enemyProjectiles.Count - 1; i >= 0; i--)
            {
                Projectile ii = (Projectile)enemyProjectiles[i];

                if (!ii.intersects(spriteBounds))
                    enemyProjectiles.Remove(ii);
            }

            for (int i = enemies.Count - 1; i >= 0; i--)
            {
                Sprite ii = (Sprite)enemies[i];

                if (!ii.intersects(spriteBounds))
                    enemies.Remove(ii);
            }

            player.KeepInside(screen);

            int temp = enemies.Count;
            //Collision Detection
            CollisionDetection cd = new CollisionDetection(player, playerProjectiles, enemies, enemyProjectiles);
            if (temp - enemies.Count != 0)
            {
                foreach (ISubscriber s in subscribers)
                {
                    s.Deliver(new ScoreChangeMessage(temp - enemies.Count));
                    enemy_death.Play();
                }
            }

            if (player.Health <= 0)
            {
                foreach (ISubscriber s in subscribers)
                {
                    s.Deliver(new GameOverMessage(GameOverMessage.Outcome.LOSS));
                }
            }

            foreach (ISubscriber s in subscribers)
            {
                s.Deliver(new PlayerHealthMessage(player.Health));
            }

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            player.Draw();
            
            foreach (Sprite s in enemies)
            {
                s.Draw();
            }

            foreach (Sprite s in playerProjectiles)
            {
                s.Draw();
            }
            foreach (Sprite s in enemyProjectiles)
            {
                s.Draw();
            }
        }

        public void AddEnemyProjectile(Projectile p)
        {
            enemyProjectiles.Add(p);
        }

        public void AddProjectile(Projectile p)
        {
            playerProjectiles.Add(p);
        }

        public void Subscribe(ISubscriber s)
        {
            subscribers.Add(s);
        }
    }
}
